package com.gosling.cloudspace.service;

import cn.dev33.satoken.stp.SaTokenInfo;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.code.kaptcha.Constants;
import com.gosling.cloudspace.tools.AESUtil;
import com.gosling.cloudspace.common.constants.CommonCodeEnum;
import com.gosling.cloudspace.common.constants.UserLoginStatusEnum;
import com.gosling.cloudspace.common.convert.DefaultConvert;
import com.gosling.cloudspace.common.entity.dtos.LoginDto;
import com.gosling.cloudspace.common.entity.pojo.UserInfoPO;
import com.gosling.cloudspace.common.entity.vo.LoginResultVO;
import com.gosling.cloudspace.common.entity.vo.UserInfoReqVO;
import com.gosling.cloudspace.common.entity.vo.UserInfoVO;
import com.gosling.cloudspace.common.exception.CommonException;
import com.gosling.cloudspace.common.service.UserInfoService;
import com.gosling.cloudspace.config.mybatis.LambdaQueryWrapperX;
import com.gosling.cloudspace.mapper.UserInfoMapper;
import com.gosling.cloudspace.tools.CompTool;
import com.gosling.cloudspace.tools.IdNoUtil;
import com.gosling.cloudspace.tools.ImgsSelectTool;
import com.gosling.cloudspace.tools.RegexTool;
import org.apache.commons.lang3.RandomStringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.util.DigestUtils;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.Date;
import java.util.List;
import java.util.Objects;

/**
 * 登录实现层
 *
 * @email 1793327033@qq.com
 * @classname UserLoginServiceImpl
 * @author: osc_lihonglin
 * @date: 2021/6/24 9:57
 */
@Service
public class UserServiceImpl extends ServiceImpl<UserInfoMapper, UserInfoPO> implements UserInfoService {
    protected final Logger logger = LoggerFactory.getLogger(getClass());
    @Resource
    private UserInfoMapper userInfoMapper;

    /**
     * 登录实现
     *
     * @param dto
     * @return
     */
    @Override
    public LoginResultVO login(HttpServletRequest request, LoginDto dto) {
        if (Objects.isNull(dto)) {
            logger.info("参数异常！data={}", dto);
            throw new CommonException(CommonCodeEnum.PARAM_INVALID);
        }
        String imageCode = dto.getImageCode();
        String phoneNumber = dto.getPhoneNumber();
        String password = dto.getPassword();
        String username = dto.getUsername();
        //验证码校验
        if (!checkValidateCodeReq(request, imageCode)) {
            logger.info("验证码异常!data={}", imageCode);
            throw new CommonException(CommonCodeEnum.VALIDATE_CODE_ERROR);
        }
        //密码参数校验
        if (StrUtil.isBlank(password)) {
            logger.info("用户名或密码错误!data={}", password);
            throw new CommonException(CommonCodeEnum.LOGIN_PASSWORD_ERROR);
        }
        LambdaQueryWrapperX<UserInfoPO> wrapper = new LambdaQueryWrapperX<>();
        //手机号密码登录
        wrapper.eq(StrUtil.isNotEmpty(phoneNumber) &&
                        RegexTool.isMobile(phoneNumber), UserInfoPO::getPhone, phoneNumber)
                //账户密码登录
                .or().eq(StrUtil.isNotEmpty(username), UserInfoPO::getUsername, username);
        UserInfoPO userInfo = userInfoMapper.selectOneX(wrapper);
        if (ObjUtil.isNull(userInfo)) {
            throw new CommonException(CommonCodeEnum.DATA_NOT_EXIST);
        }
        String pswd = DigestUtils.md5DigestAsHex((dto.getPassword() + userInfo.getSalt()).getBytes());
        if (!userInfo.getPassword().equals(pswd)) {
            throw new CommonException("用户名或密码错误");
        }

        //登录状态，online-正常;lock-锁定;offline-退出;logout-注销
        userInfo.setStatus(UserLoginStatusEnum.ONLINE.code);
        userInfoMapper.updateById(userInfo);

        //saToken 存入用户id
        StpUtil.setLoginId(userInfo.getUserId());
        SaTokenInfo tokenInfo = StpUtil.getTokenInfo();
        UserInfoVO userInfoVO = DefaultConvert.INSTANCE.convertUserPO2VO(userInfo);
        //用户信息过期 延时10秒
//        Redis.getStringTemplate().opsForValue().set(RedisConstant.APP_LOGIN_USER_INFO + userId, JSONUtil.toJsonStr(userInfoVO), tokenInfo.getTokenTimeout() + 10, TimeUnit.SECONDS);

        LoginResultVO resultVO = new LoginResultVO();
        resultVO.setTokenInfo(tokenInfo);
        resultVO.setUserInfo(userInfoVO);
        return resultVO;
    }

    /**
     * 注册接口
     *
     * @param dto
     * @return
     */
    @Override
    public void registry(HttpServletRequest request, LoginDto dto) {
        logger.info("入参:{}", JSON.toJSONString(dto));
        //参数校验,必须满足三者都不为空
        if (StrUtil.isNotEmpty(dto.getUsername())
                || StrUtil.isNotEmpty(dto.getPassword())
                || StrUtil.isNotEmpty(dto.getPhoneNumber())
                || StrUtil.isNotEmpty(dto.getImageCode())) {
            logger.info("参数异常！");
            throw new CommonException(CommonCodeEnum.LOGIN_PASSWORD_ERROR);
        }
        //验证码校验
        checkValidateCodeReq(request, dto.getImageCode());
        //去查询数据库是否有该用户信息
        LambdaQueryWrapperX<UserInfoPO> wrapper = new LambdaQueryWrapperX<>();
        wrapper.eq(UserInfoPO::getPhone, dto.getPhoneNumber());
        UserInfoPO userInfoPO = userInfoMapper.selectOneX(wrapper);
        if (Objects.nonNull(userInfoPO)) {
            //如果不为空，那么说明该用户信息已存在
            logger.info("该手机号已存在");
            throw new CommonException(CommonCodeEnum.USER_ERROR);
        }
        wrapper.clear();
        wrapper.eq(UserInfoPO::getUsername, dto.getUsername());
        userInfoPO = userInfoMapper.selectOneX(wrapper);
        if (Objects.nonNull(userInfoPO)) {
            //如果不为空，那么说明该用户信息已存在
            logger.info("该用户名已存在");
            throw new CommonException(CommonCodeEnum.USER_ERROR);
        }
        UserInfoPO userInfoRegistry = new UserInfoPO();
        String salt = RandomStringUtils.randomAlphabetic(10);
        userInfoRegistry.setUserId(IdNoUtil.nextId("U"));
        userInfoRegistry.setSalt(salt);
        userInfoRegistry.setPassword(DigestUtils.md5DigestAsHex((dto.getPassword() + salt).getBytes()));
        userInfoRegistry.setName("小明");
        userInfoRegistry.setSex("0");
        userInfoRegistry.setFlag("1");
        userInfoRegistry.setStatus(UserLoginStatusEnum.OFFLINE.code);
        userInfoRegistry.setFanNum("0");
        userInfoRegistry.setFollowNum("0");
        userInfoRegistry.setIsCertification("0");
        userInfoRegistry.setCreateTime(new Date());
        userInfoRegistry.setUpdateTime(new Date());
        userInfoRegistry.setPhone(AESUtil.encrypt(dto.getPhoneNumber()));
        userInfoRegistry.setUsername(dto.getUsername());
        userInfoRegistry.setIsIdentityAuthentication("0");
        userInfoRegistry.setImage(ImgsSelectTool.getHeaderImgFile());
        userInfoRegistry.setEquipmentId(RandomStringUtils.randomAlphabetic(5));
        userInfoRegistry.setPersonalSignature("这家伙很懒,还没有设置个性签名");
        userInfoMapper.insert(userInfoRegistry);
        logger.info("用户注册成功");
    }

    /**
     * 获取当前登录状态等信息
     *
     * @return
     */
    @Override
    public UserInfoVO localUserInfo() {
        String userId = StpUtil.getLoginIdAsString();
        UserInfoPO userInfo = userInfoMapper.byUserId(userId);
        //获取JWT存在当前线程的用户
        if (ObjUtil.isNull(userInfo)) {
            logger.error("未查询到当前用户");
            throw new CommonException();
        }
        logger.error("操作成功");
        UserInfoVO userInfoVO = DefaultConvert.INSTANCE.convertUserPO2VO(userInfo);
        return userInfoVO;
    }

    /**
     * 获取首页user-profile页面用户信息
     *
     * @return
     */
    @Override
    public UserInfoVO getUserInfo(UserInfoReqVO vo) {
        String userId = vo.getUserId();
        UserInfoPO userInfoPO = userInfoMapper.byUserId(userId);
        if (ObjUtil.isNull(userInfoPO)) {
            logger.error("未查询到当前用户");
            throw new CommonException("未查询到当前用户");
        }
        userInfoPO.setImage(ImgsSelectTool.getHeaderImgFile());
        logger.info("操作成功");
        UserInfoVO userInfoVO = DefaultConvert.INSTANCE.convertUserPO2VO(userInfoPO);
        return userInfoVO;
    }

    /**
     * 获取所有用户信息
     *
     * @return
     */
    @Override
    public List<UserInfoVO> getUserInfoAll() {
        LambdaQueryWrapper<UserInfoPO> wrapper = Wrappers.lambdaQuery();
        List<UserInfoPO> userInfoList = userInfoMapper.selectList(wrapper);
        if (CollectionUtil.isEmpty(userInfoList)) {
            logger.error("未查询到当前用户");
            throw new CommonException("未查询到当前用户");
        }
        for (UserInfoPO userInfoPO : userInfoList) {
            userInfoPO.setPhone(CompTool.getPhoneNumberShield(userInfoPO.getPhone()));
        }
        logger.info("操作成功");
        return DefaultConvert.INSTANCE.convertUserListPO2VO(userInfoList);
    }

    /**
     * 退出登录
     *
     * @return
     */
    @Override
    public void offline() {
        String loginId = StpUtil.getLoginIdAsString();
        if (StrUtil.isBlank(loginId)) {
            logger.error("用户缓存信息已过期");
            throw new CommonException(CommonCodeEnum.TOKEN_INVALID);
        }
        UserInfoPO userInfo = baseMapper.byUserId(loginId);
        if (Objects.isNull(userInfo)) {
            logger.error("用户信息不存在");
            throw new CommonException(CommonCodeEnum.TOKEN_EXPIRE);
        }
        //登录状态，online-正常;lock-锁定;offline-退出;logout-注销
        userInfo.setStatus(UserLoginStatusEnum.OFFLINE.code);
        userInfoMapper.updateById(userInfo);

        //退出登录
        StpUtil.logout();
        //删除用户信息缓存
        //StpRedisTemplateUtil.getStringTemplate().delete(RedisConstant.APP_LOGIN_USER_INFO + loginId);

        logger.info("已删除用户token，用户退出成功，状态已更新");
    }

    /**
     * 注销
     */
    @Override
    public void logout() {
        String loginId = StpUtil.getLoginIdAsString();
        if (StrUtil.isBlank(loginId)) {
            logger.error("用户缓存信息已过期");
            throw new CommonException(CommonCodeEnum.TOKEN_INVALID);
        }
        UserInfoPO userInfo = baseMapper.byUserId(loginId);
        if (Objects.isNull(userInfo)) {
            logger.error("用户信息不存在");
            throw new CommonException(CommonCodeEnum.TOKEN_EXPIRE);
        }
        //登录状态，online-正常;lock-锁定;offline-退出;logout-注销
        userInfo.setStatus(UserLoginStatusEnum.LOGOUT.code);
        userInfoMapper.updateById(userInfo);

        //退出登录
        StpUtil.logout();
        //删除用户信息缓存
        //StpRedisTemplateUtil.getStringTemplate().delete(RedisConstant.APP_LOGIN_USER_INFO + loginId);

        //TODO 删除用户信息
        logger.info("已删除用户token，用户退出成功，状态已更新");
    }

    /**
     * 验证码
     *
     * @param validateCode
     */
    protected boolean checkValidateCodeReq(HttpServletRequest request, String validateCode) {
        String kaptcha = (String) request.getSession().getAttribute(Constants.KAPTCHA_SESSION_KEY);
        request.getSession().removeAttribute(Constants.KAPTCHA_SESSION_KEY);
        if (StrUtil.isBlank(validateCode) || !validateCode.equalsIgnoreCase(kaptcha)) {
            logger.error("验证码校验失败");
            return false;
        }
        logger.error("验证码校验成功");
        return true;
    }

}
